package com.ideaaedi.zoo.example.springboot.dynamic.datasource.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.ideaaedi.commonmp.lite.transaction.DSManualTransactionSupport;
import com.ideaaedi.commonspring.transaction.DefaultManualTransactionSupport;
import com.ideaaedi.zoo.example.springboot.dynamic.datasource.mapper.MysqlStudentMapper;
import com.ideaaedi.zoo.example.springboot.dynamic.datasource.mapper.OracleTeacherMapper;
import com.ideaaedi.zoo.example.springboot.dynamic.datasource.po.StudentPO;
import com.ideaaedi.zoo.example.springboot.dynamic.datasource.po.TeacherPO;
import jakarta.annotation.Resource;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.sql.SQLException;
import java.util.List;

/**
 * 可以打断点观察数据，进行分析
 * <p>
 *   结论1：DefaultManualTransactionSupport 相当于 @Transactional ，内部逻辑只涉及到单数据源的逻辑;  DefaultManualTransactionSupport 能替代@DSTransactional，但不能替代@DSTransactional
 *         即：DefaultManualTransactionSupport内部代码使用到的数据源，只能和DefaultManualTransactionSupport.exec/voidExec当时所
 *            代表的数据源的保持一致，当尝试切换至其它数据源时，会切换(静默)失败，
 *            仍然使用和DefaultManualTransactionSupport.exec/voidExec当时所代表的数据源
 *   结论2：：DSManualTransactionSupport 相当于@DSTransactional;     DSManualTransactionSupport 能替代@DSTransactional，也能代替@Transactional。
 * </p>
 */
@SuppressWarnings("removal")
@Component
public class BizService4Trans {
    
    @Resource
    private MysqlStudentMapper mysqlStudentMapper;
    
    @Resource
    private OracleTeacherMapper oracleTeacherMapper;
    
    public void save(StudentPO student) {
        mysqlStudentMapper.insert(student);
    }
    
    public List<StudentPO> listStudent() {
        return mysqlStudentMapper.list();
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void clearStudent() {
        mysqlStudentMapper.deleteAll();
    }
    
    @DS("oracle")
    public void save(TeacherPO teacher) {
        oracleTeacherMapper.insert(teacher);
    }
    
    @DS("oracle")
    public List<TeacherPO> listTeacher() {
        return oracleTeacherMapper.list();
    }
    
    @DS("oracle")
    @Transactional(rollbackFor = Exception.class)
    public void clearTeacher() {
        oracleTeacherMapper.deleteAll();
    }
    
    //    -------------------------- mysql部分 【测试：手动事务中的代码，只涉及到单个数据源】 --------------------------
    public void insertStudent(StudentPO student) {
        mysqlStudentMapper.deleteAll();
        DefaultManualTransactionSupport defaultManualTransactionSupport = DefaultManualTransactionSupport.create();
        try {
            System.err.println("当前数据源是：" + defaultManualTransactionSupport.transactionManager().getDataSource());
            System.err.println("当前数据源对应的链接是：" + defaultManualTransactionSupport.transactionManager().getDataSource().getConnection());
        } catch (SQLException e) {
            // ignore
        }
        try {
            defaultManualTransactionSupport.voidExec(() -> {
                mysqlStudentMapper.insert(student);
                Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(mysqlStudentMapper.list().size() == 0, "student should be empty.");
        mysqlStudentMapper.deleteAll();
    }
    
    public void insertStudent2(StudentPO student) {
        mysqlStudentMapper.deleteAll();
        try {
            DSManualTransactionSupport.create().voidExec(() -> {
                mysqlStudentMapper.insert(student);
                Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(mysqlStudentMapper.list().size() == 0, "student should be empty.");
        mysqlStudentMapper.deleteAll();
    }
    
    public void deleteAllStudent() {
        mysqlStudentMapper.deleteAll();
        mysqlStudentMapper.insert(StudentPO.builder().id(1).name("夏洛").build());
        Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
        DefaultManualTransactionSupport defaultManualTransactionSupport = DefaultManualTransactionSupport.create();
        try {
            System.err.println("当前数据源是：" + defaultManualTransactionSupport.transactionManager().getDataSource());
            System.err.println("当前数据源对应的链接是：" + defaultManualTransactionSupport.transactionManager().getDataSource().getConnection());
        } catch (SQLException e) {
            // ignore
        }
        try {
            defaultManualTransactionSupport.voidExec(() -> {
                mysqlStudentMapper.deleteAll();
                Assert.isTrue(mysqlStudentMapper.list().size() == 0, "student should be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
        mysqlStudentMapper.deleteAll();
    }
    
    public void deleteAllStudent2() {
        mysqlStudentMapper.deleteAll();
        mysqlStudentMapper.insert(StudentPO.builder().id(1).name("夏洛").build());
        Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
        try {
            DSManualTransactionSupport.create().voidExec(() -> {
                mysqlStudentMapper.deleteAll();
                Assert.isTrue(mysqlStudentMapper.list().size() == 0, "student should be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(mysqlStudentMapper.list().size() > 0, "student should not be empty.");
        mysqlStudentMapper.deleteAll();
    }
    
    //    -------------------------- oracle部分 【测试：手动事务中的代码，只涉及到单个数据源】 --------------------------
    @DS("oracle")
    public void insertTeacher(TeacherPO teacher) {
        oracleTeacherMapper.deleteAll();
        DefaultManualTransactionSupport defaultManualTransactionSupport = DefaultManualTransactionSupport.create();
        try {
            System.err.println("当前数据源是：" + defaultManualTransactionSupport.transactionManager().getDataSource());
            System.err.println("当前数据源对应的链接是：" + defaultManualTransactionSupport.transactionManager().getDataSource().getConnection());
        } catch (SQLException e) {
            // ignore
        }
        try {
            defaultManualTransactionSupport.voidExec(() -> {
                oracleTeacherMapper.insert(teacher);
                Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(oracleTeacherMapper.list().size() == 0, "teacher should be empty.");
        oracleTeacherMapper.deleteAll();
    }
    
    @DS("oracle")
    public void insertTeacher2(TeacherPO teacher) {
        oracleTeacherMapper.deleteAll();
        try {
            DSManualTransactionSupport.create().voidExec(() -> {
                oracleTeacherMapper.insert(teacher);
                Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(oracleTeacherMapper.list().size() == 0, "teacher should be empty.");
        oracleTeacherMapper.deleteAll();
    }
    
    @DS("oracle")
    public void deleteAllTeacher() {
        oracleTeacherMapper.deleteAll();
        oracleTeacherMapper.insert(TeacherPO.builder().id(1).name("蒋老师").build());
        Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
        DefaultManualTransactionSupport defaultManualTransactionSupport = DefaultManualTransactionSupport.create();
        try {
            System.err.println("当前数据源是：" + defaultManualTransactionSupport.transactionManager().getDataSource());
            System.err.println("当前数据源对应的链接是：" + defaultManualTransactionSupport.transactionManager().getDataSource().getConnection());
        } catch (SQLException e) {
            // ignore
        }
        try {
            defaultManualTransactionSupport.voidExec(() -> {
                oracleTeacherMapper.deleteAll();
                Assert.isTrue(oracleTeacherMapper.list().size() == 0, "teacher should be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
        oracleTeacherMapper.deleteAll();
    }
    
    @DS("oracle")
    public void deleteAllTeacher2() {
        oracleTeacherMapper.deleteAll();
        oracleTeacherMapper.insert(TeacherPO.builder().id(1).name("蒋老师").build());
        Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
        try {
            DSManualTransactionSupport.create().voidExec(() -> {
                oracleTeacherMapper.deleteAll();
                Assert.isTrue(oracleTeacherMapper.list().size() == 0, "teacher should be empty.");
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (Exception e) {
            // ignore
        }
        Assert.isTrue(oracleTeacherMapper.list().size() > 0, "teacher should not be empty.");
        oracleTeacherMapper.deleteAll();
    }
    
    /**
     * 【验证成功】当内部逻辑涉及多个数据源时，DefaultManualTransactionSupport管理多数据源事务失效（只能管理单数据源）
     * <br />
     * DefaultManualTransactionSupport 只相当于 @Transactional, 不能替代@DSTransactional。DefaultManualTransactionSupport内部代码使用到的数据源，
     * 只能和DefaultManualTransactionSupport.exec/voidExec当时所代表的数据源的保持一致，当尝试切换至其它数据源时，会切换(静默)失败，
     * 仍然使用和DefaultManualTransactionSupport.exec/voidExec当时所代表的数据源
     */
    public void insertStudentAndTeacherThanOccurException() {
        DefaultManualTransactionSupport defaultManualTransactionSupport = DefaultManualTransactionSupport.create();
        try {
            System.err.println("当前数据源是：" + defaultManualTransactionSupport.transactionManager().getDataSource());
            System.err.println("当前数据源对应的链接是：" + defaultManualTransactionSupport.transactionManager().getDataSource().getConnection());
        } catch (SQLException e) {
            // ignore
        }
        defaultManualTransactionSupport.voidExec(() -> {
            BizService4Trans bizService = (BizService4Trans) AopContext.currentProxy();
        
            // 插入
            bizService.save(StudentPO.builder().id(1).name("夏洛").build());
            System.out.println("插入mysql student完成了.");
        
            bizService.save(TeacherPO.builder().id(1).name("蒋老师").build());
            System.out.println("插入oracle teacher完成了.");
        });
    }
    
    /**
     * 【验证成功】当内部逻辑涉及多个数据源时，DSManualTransactionSupport可以管理多数据源事务
     */
    public void insertStudentAndTeacherThanOccurException2() {
        BizService4Trans bizService = (BizService4Trans) AopContext.currentProxy();
        try {
            DSManualTransactionSupport.create().voidExec(() -> {
                // 插入
                bizService.save(StudentPO.builder().id(1).name("夏洛").build());
                System.out.println("插入mysql student完成了.");
            
                bizService.save(TeacherPO.builder().id(1).name("蒋老师").build());
                System.out.println("插入oracle teacher完成了.");
            
                Assert.isTrue(bizService.listStudent().size() > 0, "student should not be empty.");
                Assert.isTrue(bizService.listTeacher().size() > 0, "teacher should not be empty.");
            
                // 使报错，促发事务回滚
                int a = 1 / 0;
            });
        } catch (ArithmeticException e) {
            // ignore
        }
        Assert.isTrue(bizService.listStudent().size() == 0, "student should be empty.");
        Assert.isTrue(bizService.listTeacher().size() == 0, "teacher should be empty.");
        bizService.clearStudent();
        bizService.clearTeacher();
    }
}